Piano notes book, powered by Astro and React.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 

118 Zeilen
2.9 KiB

  1. import createVerovioModule from 'verovio/wasm';
  2. import { VerovioToolkit } from 'verovio/esm';
  3. import { readFile, readdir } from 'node:fs/promises';
  4. import { JSDOM } from 'jsdom';
  5. import type {APIRoute, GetStaticPaths} from 'astro';
  6. const filter = (musicXml: string) => {
  7. const jsdom = new JSDOM(musicXml, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
  8. const win = jsdom.window;
  9. // const win = jsdom.window;
  10. return win.document.documentElement.outerHTML;
  11. };
  12. const processOutput = (xmlData: string) => {
  13. const jsdom = new JSDOM(xmlData, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
  14. const win = jsdom.window;
  15. const [svgElemsRoot, svgElemsMain] = Array.from(win.document.getElementsByTagName('svg'));
  16. if (typeof svgElemsRoot === 'undefined') {
  17. return '';
  18. }
  19. if (typeof svgElemsMain === 'undefined') {
  20. return '';
  21. }
  22. // svgElemsRoot.getAttributeNames().forEach((a) => {
  23. // const attr = svgElemsRoot.getAttribute(a);
  24. // if (!attr) {
  25. // return;
  26. // }
  27. // svgElemsMain.setAttribute(a, attr);
  28. // });
  29. Array.from(svgElemsRoot.children).forEach((h) => {
  30. if (h !== svgElemsMain) {
  31. h.remove();
  32. if (h.tagName.toLowerCase() === 'desc') {
  33. return;
  34. }
  35. if (svgElemsMain.children[0]) {
  36. svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
  37. return;
  38. }
  39. svgElemsMain.appendChild(h);
  40. }
  41. });
  42. Array.from(win.document.getElementsByClassName('pgHead')).forEach(h => {
  43. h.remove();
  44. });
  45. Array.from(win.document.getElementsByClassName('pgFoot')).forEach(h => {
  46. h.remove();
  47. });
  48. Array.from(win.document.getElementsByTagName('defs')).forEach(h => {
  49. h.remove();
  50. if (svgElemsMain) {
  51. if (svgElemsMain.children[0]) {
  52. svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
  53. } else {
  54. svgElemsMain.appendChild(h);
  55. }
  56. }
  57. });
  58. return `<?xml version="1.0" encoding="utf-8"?>${svgElemsMain.outerHTML}`
  59. };
  60. export const GET: APIRoute = async ({ params }) => {
  61. const verovioModule = await createVerovioModule();
  62. const score = await readFile(`public/scores/${params.asset}.musicxml`, 'utf-8');
  63. const verovioToolkit = new VerovioToolkit(verovioModule);
  64. const filteredScore = filter(score);
  65. const isSuccessful = verovioToolkit.loadData(filteredScore);
  66. if (!isSuccessful) {
  67. return new Response(null, { status: 500 });
  68. }
  69. verovioToolkit.setOptions({
  70. breaks: 'none',
  71. font: 'Bravura',
  72. });
  73. let data: string;
  74. try {
  75. const raw = verovioToolkit.renderToSVG(1)
  76. .replace(/xmlns:mei="(.+?)"/g, '')
  77. .replace(/xlink:/g, '');
  78. data = processOutput(raw);
  79. } catch (err) {
  80. console.error(err);
  81. return new Response(null, { status: 500 });
  82. }
  83. return new Response(
  84. data,
  85. {
  86. headers: {
  87. 'Content-Type': 'image/svg+xml',
  88. },
  89. status: 200,
  90. }
  91. );
  92. };
  93. export const getStaticPaths: GetStaticPaths = async () => {
  94. const files = await readdir('public/scores');
  95. return files
  96. .filter((f) => f.endsWith('.musicxml'))
  97. .map((f) => ({
  98. params: {
  99. asset: f.replace(/\.musicxml/g, ''),
  100. },
  101. }));
  102. };